Add send_keys_batch and clarify terminal workflow primitives#78
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #78 +/- ##
==========================================
- Coverage 85.07% 84.97% -0.10%
==========================================
Files 42 42
Lines 2881 3042 +161
Branches 385 412 +27
==========================================
+ Hits 2451 2585 +134
- Misses 322 339 +17
- Partials 108 118 +10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
why: Agents need ordered raw terminal input without turning command completion into a heterogeneous workflow DSL. what: - Add typed send_keys_batch operation and result models - Register the tool as mutating open-world shell input - Return per-operation success and failure metadata with stop/continue behavior - Redact nested batch key payloads in audit summaries - Route runtime prompt guidance toward run_command and capture_since boundaries
why: send_keys_batch needs explicit regression coverage for sequencing, errors, and client-facing metadata. what: - Add functional tests for ordered batch sends and stop/continue failures - Cover nested audit redaction for batch operation payloads - Update prompt and server instruction tests for run_command-first guidance - Pin open-world registration and safety visibility for send_keys_batch
why: Agents and operators need clear boundaries between authored commands, raw input, and repeated observation. what: - Add the send_keys_batch tool page and navigation entries - Reframe prompts, quickstart, recipes, gotchas, and tool docs around run_command and capture_since - Keep wait_for_channel documented as the low-level custom-completion escape hatch - Include new batch/result models in generated API documentation config
why: The project overview should match the command/observation architecture exposed by the tools. what: - List send_keys_batch in the pane tool catalog - Add running and driving workflow summaries - Clarify that raw input batching is separate from command completion and observation
3c633b1 to
df3e19c
Compare
why: Unknown keys in nested send_keys_batch operations were ignored by Pydantic, so misspelled targets could fall back to the default pane. what: - Forbid extra fields on SendKeysOperation - Add parametrized validation coverage for unsupported operation fields
why: Audit summaries are built before schema validation reports malformed send_keys_batch payloads, so non-dict operation entries must not be logged verbatim. what: - Replace malformed nested operation entries with type metadata - Add parametrized audit redaction coverage for malformed entries
why: Audit summaries are built before schema validation rejects malformed send_keys_batch inputs, so invalid operation shapes must not preserve raw payload text. what: - Redact non-list operations payloads by shape - Redact unknown or wrong-typed nested operation fields - Cover malformed operation payloads with parametrized regression tests
why: Exceptions from internal operations carry useful recovery suggestions that were being discarded in the stringification process. what: - Check for suggestion attribute before stringifying ToolError and ExpectedToolError - Append suggestion to error string if present to aid MCP clients - Add test coverage for suggestion preservation
why: Prevent operations from hanging indefinitely and consuming agent tokens what: - Add cooperative timeout check to send_keys_batch iteration - Abort batch execution when timeout is exceeded - Add test coverage for batch timeouts
why: FastMCP documentation standards require {tooliconl} for inline references
what:
- Update send_keys_batch references in concepts.md and safety.md to use {tooliconl}
- Update wait_for_channel references in wait-for-channel.md to use {tooliconl}
- Retain plain backticks in headings where custom Sphinx roles cause TOC generator crashes
Code reviewNo issues found. Checked for bugs and AGENTS.md compliance. 🤖 Generated with Claude Code |
why: The closing sentence narrated the docs/prompt-recipe reframing and re-announced run_command, which shipped in 0.1.0a12 — both restate change history the diff and commit log already carry. what: - Drop the "docs and prompt recipes now route ... rather than ..." sentence from the unreleased What's new entry - Keep the send_keys_batch capability description and the run_command / capture_since scoping guidance
why: Malformed send_keys_batch schema errors can expose raw key payloads before audit redaction runs. what: - Add a strict xfail regression through the production FastMCP server - Assert validation result text and warning logs do not echo payloads
why: FastMCP logs and tool error results render Pydantic validation inputs before send_keys_batch audit redaction can run. what: - Format schema validation errors without rejected input values - Redact FastMCP invalid-argument log records before handlers render - Convert the strict xfail payload-leak case to a passing regression
why: send_keys_batch only checks the batch deadline before an operation starts, so a stalled send can exceed timeout and still report success. what: - Add a strict xfail regression for a single blocked send operation - Keep the mocked subprocess timeout dormant until the timed send path exists
Code reviewRe-reviewed the commits added since the previous review: the 🤖 Generated with Claude Code |
why: Timed batches must bound the tmux send operation itself, not just skip operations whose start time is already past the batch deadline. what: - Send timed batch operations through subprocess.run with remaining timeout - Keep untimed sends on the existing libtmux send_keys path - Convert the strict xfail timeout regression into a passing test
why: The unreleased notes predated the batch error-handling and timeout affordances and the argument-validation input redaction, all of which are user-facing. what: - Note send_keys_batch stop/continue handling and the optional timeout in the existing What's new entry - Add a Fixes entry: argument-validation failures no longer echo rejected input into logs or error text
Summary
send_keys_batch— sends an ordered batch of raw key/text operations to tmux panes and returns a typed per-operation result (index, resolved pane, success, error, elapsed).on_errorselects stop-at-first-failure or continue-and-report, an optionaltimeoutaborts a long-running batch, and every operation carries its own pane/window targeting.send → wait → capture. Authored command completion stays withrun_command; repeated observation stays withcapture_since. This preserves clean per-operation error attribution instead of growing a workflow DSL inside one tool.operations[].keysare digested to a length + SHA-256 prefix exactly like top-levelsend_keys, and malformed or schema-violating operation payloads are redacted by shape before the audit log can persist a raw value from an invalid request.suggestionhint from the underlyingToolError, so a client recovering from one failed operation gets the same guidance a singlesend_keyscall would.run_command, raw TUI / persistent-shell input throughsend_keys/send_keys_batch, and repeated observation throughcapture_since, withwait_for_channelreserved for custom shell completion.Changes by area
Tool surface
src/libtmux_mcp/tools/pane_tools/io.py:send_keys_batchruntime — ordered iteration,on_errorhandling, a cooperativetimeoutchecked between operations, and suggestion-preserving error capture.src/libtmux_mcp/models.py:SendKeysOperation(strict,extra="forbid"),SendKeysOperationResult, andSendKeysBatchResult.src/libtmux_mcp/tools/pane_tools/__init__.py,server.py: register the tool at the mutating tier and refresh the server instructions.Audit middleware
src/libtmux_mcp/middleware.py: digest nestedoperations[].keys; redact malformed, non-list, and unknown-field operation payloads by shape before they can be logged.Prompts & docs
src/libtmux_mcp/prompts/recipes.py: rewriterun_and_waitto teachrun_commandinstead of a manualsend_keys+wait_for_channel+capture_panesequence.docs/: newsend-keys-batchtool page; prompts, quickstart, recipes, gotchas, and topic guides reframed around the three primitives; Sphinx cross-reference roles fixed for the new tools.README.md,CHANGES: workflow overview and unreleased changelog entry.Tests
tests/test_pane_tools.py: ordering, stop/continue, empty rejection, unknown-field rejection, timeout abort, suggestion preservation, and docstring routing.tests/test_middleware.py: redaction of well-formed, malformed, and nonschema batch payloads.tests/test_prompts.py,tests/test_server.py: updated prompt template and server-instruction expectations.Design decisions
send_keys_batchdeliberately refuses heterogeneoussend → wait → capturesteps. Mixing operation kinds would blur per-operation error attribution and grow an ad-hoc orchestration language inside a single MCP tool; authored completion already hasrun_commandand observation hascapture_since.SendKeysOperationdicts. It checks each field by type and redacts anything unexpected — a bare string, a non-dict list item, or an unknown key can never leak a payload into the log.timeoutis evaluated between operations rather than interrupting an in-flightsend_keys. tmux key delivery is effectively instantaneous, so the guard exists to bound a long batch and stop spending agent tokens — not to kill a single hung send.Test plan
uv run ruff check .,uv run ruff format .,uv run mypy .,uv run py.test --reruns 0,just build-docstest_send_keys_batch_sends_operations_in_order— operations dispatch in submitted order with per-operation resultstest_send_keys_batch_stops_after_operation_error/test_send_keys_batch_continues_after_operation_error—on_errorsemantics andstopped_attest_send_keys_batch_rejects_empty_operations— empty batch raises an expected errortest_send_keys_operation_rejects_unknown_fields—extra="forbid"rejects misnamed fieldstimeoutabort and per-operation suggestion preservationtest_summarize_args_redacts_send_keys_batch_operationsplus malformed / nonschema payload redactiontest_send_keys_docstring_routes_authored_commands_to_run_command— docstring routes authored commands torun_commandCloses #49.
Closes #61.
Refs #50.